﻿using ServerCommon.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ServerCommon.Net
{
    /// <summary>
    /// 每个节点（service/customer）都有一个
    /// 防止内存碎片，全局只有一个，接受、发送共用一个大内存
    /// 服务器与客户端、服务器与服务器之间通信的socket缓存都在这里分配
    /// </summary>
    public class BufferManager
    {
        private static readonly LogHelper _log = LogHelper.GetLogger(typeof(BufferManager));

        /// <summary>
        /// 一个buffer最大1024M
        /// </summary>
        private const long MaxArraySize = 1024 * 1024 * 1024;

        // 总内存大小
        private readonly long _capability;

        // 总内存列表
        private readonly List<byte[]> _bufferBlockList;

        /// <summary>
        /// 可用buffer的索引
        /// </summary>
        private int _currentIndex;

        /// <summary>
        /// 可用的buffer list的索引
        /// </summary>
        private int _listIndex;

        /// <summary>
        /// 已经使用的缓存大小：从setBuffer中分配出的所有缓存的大小
        /// </summary>
        private long _usedCount;

        public int BufferSizeForEachBlock { get; private set; }

        public BufferManager(long capacity, int buffSizeForEachBlock)
        {
            _capability = capacity;
            BufferSizeForEachBlock = buffSizeForEachBlock;
            _currentIndex = 0;
            _listIndex = 0;
            _bufferBlockList = new List<byte[]>();
        }

        public void InitBuffer()
        {
            var tempCapacity = _capability;
            do
            {
                byte[] block = tempCapacity > MaxArraySize ? new byte[MaxArraySize] : new byte[tempCapacity];
                _bufferBlockList.Add(block);
                tempCapacity -= MaxArraySize;

            } while (tempCapacity > 0);

        }

        // 为指定的SocketAsyncEventArgs分配缓存
        public bool SetBuffer(SocketAsyncEventArgs saea)
        {
            // 如果当前可分配内存已用尽
            if (_capability < _usedCount + BufferSizeForEachBlock)
            {
                _log.ErrorFormat("BufferManager does not have enough buffer _capability:{0}, _usedCount: {1}, BufferSizeForEachBlock: {2}",
                    _capability, _usedCount, BufferSizeForEachBlock);
                return false;
            }

            if (_listIndex >= _bufferBlockList.Count)
            {
                _log.ErrorFormat("BufferManager bufferListIndex is bigger than bufferCount: _ListIndex: {0}, _bufferBlockList.Count: {1}",
                    _listIndex, _bufferBlockList.Count);
                return false;
            }

            var bufferBlock = _bufferBlockList[_listIndex];
            saea.SetBuffer(bufferBlock, _currentIndex, BufferSizeForEachBlock);
            _currentIndex += BufferSizeForEachBlock;
            _usedCount += BufferSizeForEachBlock;
            if (_currentIndex >= MaxArraySize)
            {
                _currentIndex = 0;
                _listIndex++;
            }
            return true;
        }
    }
}
